package com.yusvn.scm.quartz.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.yitter.idgen.YitIdHelper;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.yusvn.scm.common.BaseService;
import com.yusvn.scm.common.constant.Const;
import com.yusvn.scm.common.kit.MyKit;
import com.yusvn.scm.common.kit.RetKit;
import com.yusvn.scm.common.model.ScmJob;
import com.yusvn.scm.common.model.ScmJobLog;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.pattern.CronPattern;
import cn.hutool.cron.task.Task;

/**
 * 定时任务
 * @author liugz
 *
 */
public class ScmJobService extends BaseService {

	ScmJob dao = new ScmJob().dao();
	ScmJobLog jobLogDao = new ScmJobLog().dao();
	
	/**
	 * 初始化，程序首次启动时调用
	 */
	public RetKit init() {
		//数据库中查询所有定时任务
		Page<ScmJob> list = dao.template("scmJob.page").paginate(1, 3000);
		
		//循环
		for(ScmJob job : list.getList()) {
			if(0 == job.getState() ) {
				addTask(Kv.create().set("jobmethod", job.getJobmethod())
						.set("jobparm", job.getJobparm())
						.set("jobcorn", job.getJobcorn())
						.set("jobid", job.getJobid())
						.set("jobname", job.getJobname()) );
			}
		}
		
		// 支持秒级别定时任务
		CronUtil.setMatchSecond(true);
		CronUtil.start(); //CronUtil.start(true); 表示守护线程模式运行。即执行的作业同定时任务线程同时结束，可以将定时任务设为守护线程，需要注意的是，此模式下会在调用stop时立即结束所有作业线程，请确保你的作业可以被中断。
		
		return RetKit.ok("初始化成功");
	}


	/**
	 * 手动执行一次
	 */
	public RetKit runOneTask(Kv kv) {
		Long jobid = kv.getLong("jobid");      //任务id
		
		//根据ID查找
		ScmJob job = dao.findById(jobid);
//		ScmJob job = dao.findFirst("select * from scm_job where jobid=? ", jobid);
		
		String classNameWithMethodName = job.getJobmethod(); //调用方法 "com.yusvn.scm.goods.fenhuo.controller.page"
		String jobparm = job.getJobparm(); //方法的参数 json格式的字符串
		String jobcorn = "手动执行"; //kv.getStr("jobcorn").trim();  //表达式 */5 * * * * *
		String jobname = job.getJobname(); //任务名称
		
		String jobmessage = null;
		String jobexception = null;
		int runState = 0;
		
		//重新组装参数 用于传递给执行方法
		Kv kv2 = Kv.by("jobparm", jobparm);
		
		try {
	    	if(StrKit.isBlank(jobparm)) {
	    		ClassUtil.invoke(classNameWithMethodName, false); //反射执行某个方法，无参
	    	}else {
	    		kv.set(MyKit.jsonStrToKv(jobparm));
	    		ClassUtil.invoke(classNameWithMethodName, false, kv2); //反射执行某个方法，有参
	    	}
	    	
	    	jobmessage = "执行成功";
			jobexception = "";
		} catch (Exception e) {
			e.printStackTrace();
			
			jobmessage = "执行失败";
			jobexception = e.getMessage();
			runState = 1;
			return RetKit.fail(jobmessage);
		}
    	
    	//定时任务执行日志
    	Db.use(Const.SCMDB).update("insert into scm_job_log(id, jobid, jobname, jobcorn, jobmethod, jobparm, "
    			+ "schedulerid, jobmessage, state, jobexception) "
    			+ "values(?, ?, ?, ?, ?, ?,   ?, ?, ?, ?)", YitIdHelper.nextId(), jobid, jobname, jobcorn, classNameWithMethodName, jobparm, 
    			null, jobmessage, runState, jobexception);
		
    	return RetKit.ok(jobmessage);
	}
	
	
	/**
	 * 加入运行的定时任务
	 */
	public RetKit addTask(Kv kv) {
		String classNameWithMethodName = kv.getStr("jobmethod"); //调用方法 "com.yusvn.scm.goods.fenhuo.controller.page"
		String jobparm = kv.getStr("jobparm"); //方法的参数 json格式的字符串
		String jobcorn = kv.getStr("jobcorn").trim();  //表达式 */5 * * * * *
		Long    jobid   = kv.getLong("jobid");      //任务id
		String jobname = kv.getStr("jobname"); //任务名称
		 
		String schedulerId = CronUtil.schedule(jobcorn, new Task() {
		    @Override
		    public void execute() {
				String jobmessage = null;
				String jobexception = null;
				int runState = 0;
				
		    	try {
		    		//ClassUtil.invoke("com.yusvn.scm.quartz.service.SysCronService", "deleteSession",  false, kv);
			    	if(StrKit.isBlank(jobparm)) {
			    		ClassUtil.invoke(classNameWithMethodName, false); //反射执行某个方法，无参
			    	}else {
			    		kv.set(MyKit.jsonStrToKv(jobparm));
			    		ClassUtil.invoke(classNameWithMethodName, false, kv); //反射执行某个方法，有参
			    	}
			    	
			    	jobmessage = "执行成功";
					jobexception = "";
				} catch (Exception e) {
					jobmessage = "执行失败";
					jobexception = e.getMessage();
					runState = 1;
				}
		    	
		    	//定时任务执行日志
		    	Db.use(Const.SCMDB).update("insert into scm_job_log(id, jobid, jobname, jobcorn, jobmethod, jobparm, "
		    			+ "schedulerid, jobmessage, state, jobexception) "
		    			+ "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", YitIdHelper.nextId(), jobid, jobname, jobcorn, classNameWithMethodName, jobparm, 
		    			null, jobmessage, runState, jobexception);
		    }
		});
		
		Db.use(Const.SCMDB).update("update scm_job set schedulerid=?, state=0 where jobid=? ", schedulerId, kv.getStr("jobid") );
		
		return RetKit.ok("启动成功").setData(Kv.create().set("state", 0).set("schedulerid", schedulerId));
	}
	
	/**
	 * 移除运行的定时任务
	 */
	public RetKit removeTask(Kv kv) {
		String schedulerId = getSchedulerid(kv.getStr("jobid") );
		CronUtil.remove(schedulerId);
		
		Db.use(Const.SCMDB).update("update scm_job set schedulerid='', state=1 where jobid=? ", kv.getStr("jobid") );
		
		return RetKit.ok("停止成功").setData(Kv.create().set("state", 1).set("schedulerid", schedulerId));
	}
	
	/**
	 * 根据数据库中的定时任务ID jobid，查找运行的定时任务ID schedulerid
	 */
	public String getSchedulerid(String jobid) {
		return Db.use(Const.SCMDB).queryStr("select schedulerid from scm_job where jobid=? ", jobid );
	}
	
	/**
	 * 更新定时任务的执行时间规则
	 * @param schedulerId 时间规则corn表达式字符串
	 * @param jobparm 
	 * @return
	 */
	public RetKit updatePattern(String schedulerId, String jobparm) {
		CronPattern pattern = new CronPattern(jobparm);
		CronUtil.updatePattern(schedulerId, pattern);
		return RetKit.ok();
	}
	
	
	public Page<ScmJob> page(Kv kv) {
		Page<ScmJob> list = dao.template("scmJob.page", kv).paginate(getPage(kv), getLimit(kv));
		return list;
	}
	
	public RetKit add(String jsonObj) {
		ScmJob job = JSON.parseObject(jsonObj, ScmJob.class);
		
		//long id = Db.use(Const.SCMDB).queryLong("select nvl(max(jobid),0)+1 cnt from scm_job ");
		job.setJobid(YitIdHelper.nextId()); //查询最大ID oracle不支持自增ID
		
		if(job.use(Const.SCMDB).save()) {
			return RetKit.ok("添加成功");
		}
		
		return RetKit.fail("添加失败");
	}
	
	/**
	 * 修改定时任务
	 * @param jsonObj
	 * @return
	 */
	public RetKit update(String jsonObj) {
		ScmJob job = JSON.parseObject(jsonObj, ScmJob.class);
		
		Kv kv = Kv.create().set("jobmethod", job.getJobmethod())
				.set("jobparm", job.getJobparm())
				.set("jobcorn", job.getJobcorn())
				.set("jobid", job.getJobid() )
				.set("jobname", job.getJobname());
		String jobparm = StrKit.isBlank(job.getJobparm()) ? "" : job.getJobparm();

		//查询原数据
		ScmJob oldJob = dao.findFirst("select * from scm_job where jobid=? ", job.getJobid() );
		String oldJobparm = StrKit.isBlank(oldJob.getJobparm()) ? "" : oldJob.getJobparm();
		
		try {
			job.remove("schedulerid", "state");   //去掉定时任务运行id、任务状态
			job.setUpdatetime(DateUtil.date() ); //更新时间
			if(job.use(Const.SCMDB).update()) {
				// 判断表达式规则或调用方法字符串或调用方法参数字符串是否改变
				if(!oldJob.getJobcorn().equals(job.getJobcorn()) 
						|| !oldJob.getJobmethod().equals(job.getJobmethod()) 
						|| !oldJobparm.equals(jobparm) ) {
					//定时任务是启动状态
					if("0".equals(oldJob.getState().toString() )) {
						removeTask(kv); //停止任务
						addTask(kv);    //开启任务
					}
				}
				
				return RetKit.ok("修改成功");
			}
		} catch (Exception e) {
			return RetKit.fail("修改失败"+e);
		}
		
		return RetKit.fail("修改失败");
	}
	
	
	/**
	 * 删除定时任务
	 * @param id
	 * @return
	 */
	public RetKit remove(String id) {
		//移除运行的定时任务
		CronUtil.remove(getSchedulerid(id));
		
		if(dao.deleteById(id)) {
			return RetKit.ok("删除成功");
		}
		return RetKit.fail("删除失败");
	}
	
	/**
	 * 批量删除定时任务
	 * @param idsStr
	 * @return
	 */
	public RetKit removeBatch(String idsStr) {
		JSONObject idsObj = JSON.parseObject(idsStr);
		JSONArray idsArr = idsObj.getJSONArray("ids");
		
		Object[][] paras = new Object[idsArr.size()][1];
		for(int i=0;i<idsArr.size();i++) {
			paras[i][0] = idsArr.get(i);
			
			//移除运行的定时任务
			CronUtil.remove(getSchedulerid(idsArr.get(i).toString()));
		}
		
		int[] cntArr = Db.use(Const.SCMDB).batch("delete from scm_job where jobid=? ", paras, 100);
		
		int cntOk = 0;
		for(int i=0; i<cntArr.length; i++) {
			cntOk = cntOk + cntArr[i];
		}
		
		if(cntArr.length>0) {
			return RetKit.ok("成功删除 "+cntOk+" 条数据");
		}
		
		return RetKit.fail("批量删除失败");
	}
	

	/**
	 * 获取定时任务运行日志
	 * @param kv
	 * @return
	 */
	public Page<ScmJobLog> getJobLog(Kv kv){
		Page<ScmJobLog> list = jobLogDao.template("scmJob.getJobLog", kv).paginate(getPage(kv), getLimit(kv));
		return list;
	}
	
}
